﻿// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace SnapObjects.Data.Odbc
{
    internal abstract class OdbcSqlAnywhereParameterSchemaProvider : RoutineParameterSchemaProvider
    {
        public OdbcSqlAnywhereParameterSchemaProvider(DataContext dataContext) : base(dataContext)
        {
        }

        protected override IList<DynamicModel> QueryParameters(
            RoutineNameInfo nameInfo, ISqlGenerationHelper helper)
        {
            var sql = this.BuildSchemaSql(nameInfo, helper);

            return _dataContext.SqlExecutor.Select<DynamicModel>(sql);
        }

        protected async override Task<IList<DynamicModel>> QueryParametersAsync(
            RoutineNameInfo nameInfo, ISqlGenerationHelper helper, CancellationToken cancellationToken)
        {
            var sql = await this.BuildSchemaSqlAsync(nameInfo, helper, cancellationToken);

            return await _dataContext.SqlExecutor.SelectAsync<DynamicModel>(sql, cancellationToken);
        }
        protected abstract string GetCheckUserQuerySql(string name);

        protected abstract string GetSchemaQuerySql(string name, string condition);

        protected override bool TryGetDefaultValue(DynamicModel param, TypeMapping mapping, out object value)
        {
            value = param.GetValue("parameter_defaultvalue");

            if (value == null)
            {
                return false;
            }
            else
            {
                if (value is string str)
                {
                    if ("null".Equals(str, StringComparison.OrdinalIgnoreCase))
                    {
                        value = null;
                    }
                    if (str.StartsWith("'") && str.EndsWith("'"))
                    {
                        value = str.Trim('\'');
                    }
                    else if (str.StartsWith("N'") && str.Length > 2)
                    {
                        var tmpValue = str.Substring(2);

                        if (tmpValue.EndsWith("'"))
                        {
                            value = tmpValue.Substring(0, tmpValue.Length - 1);
                        }
                    }
                }

                if (value != null && mapping != null && mapping.ClrType != typeof(string))
                {
                    try
                    {
                        value = mapping.ChangeDbType(value);
                    }
                    catch
                    {
                        return false;
                    }
                }

                return true;
            }
        }

        private string BuildSchemaSql(RoutineNameInfo nameInfo, ISqlGenerationHelper helper)
        {
            var condition = String.Empty;

            var name = helper.RemoveDelimitIdentifier(nameInfo.Name);

            if (!String.IsNullOrWhiteSpace(nameInfo.Schema))
            {
                var schema = helper.RemoveDelimitIdentifier(nameInfo.Schema);

                condition = $" AND lower(u.name) = lower('{schema}') ";
            }
            else
            {
                var sql = this.GetCheckUserQuerySql(name);

                var result = _dataContext.SqlExecutor.Scalar<string>(sql);

                var isCurrentUser = !String.IsNullOrWhiteSpace(result);

                if (isCurrentUser)
                {
                    condition = " AND pro.creator = user_id()";
                }
            }

            return this.GetSchemaQuerySql(name, condition);
        }

        private async Task<string> BuildSchemaSqlAsync(
            RoutineNameInfo nameInfo, ISqlGenerationHelper helper, CancellationToken cancellationToken)
        {
            var condition = String.Empty;

            var name = helper.RemoveDelimitIdentifier(nameInfo.Name);

            if (!String.IsNullOrWhiteSpace(nameInfo.Schema))
            {
                var schema = helper.RemoveDelimitIdentifier(nameInfo.Schema);

                condition = $" AND lower(u.name) = lower('{schema}')";
            }
            else
            {
                var sql = this.GetCheckUserQuerySql(name);

                var result = await _dataContext.SqlExecutor.ScalarAsync<string>(sql, cancellationToken);

                var isCurrentUser = !String.IsNullOrWhiteSpace(result);

                if (isCurrentUser)
                {
                    condition = " AND pro.creator = user_id()";
                }
            }
           
            return this.GetSchemaQuerySql(name, condition);
        }
    }
}
